const express = require("express");
const expressWebSocket = require("express-ws");
const ffmpegPath = require('@ffmpeg-installer/ffmpeg').path;
const ffmpeg = require("fluent-ffmpeg");
ffmpeg.setFfmpegPath(ffmpegPath);
const webSocketStream = require("websocket-stream/stream");
const path = require("path");

const app = express();
app.use(express.static(__dirname));
expressWebSocket(app, null, {
  perMessageDeflate: true
});

// 支持H265转H264的WebSocket端点
app.ws("/h265-to-h264/:id", h265ToH264RequestHandle);
app.listen(8888);
console.log("Project is running at http://localhost:8888/");

app.use(express.static('public'))

/**
 * 处理H265到H264的转码和流式传输
 */
function h265ToH264RequestHandle(ws, req) {
  const stream = webSocketStream(ws, {
    binary: true,
    browserBufferTimeout: 1000000
  }, {
    browserBufferTimeout: 1000000
  });

  let url = req.query.url;
  console.log(`Input URL: "${url}"`);
  console.log("Request params:", req.params);

  try {
    // 创建FFmpeg命令，处理H265视频流并转换为H264编码的FLV格式
    const ffmpegCommand = ffmpeg(url);
    
    // 根据URL类型添加不同的输入选项
    if (url.startsWith('rtsp://') || url.startsWith('rtmp://')) {
      // RTSP或RTMP流选项
      ffmpegCommand.addInputOption([
        "-rtsp_transport", "tcp", 
        "-re"  // 按照流的原始帧率读取
      ]);
    } else if (url.startsWith('https://') || url.startsWith('http://')) {
      // HTTP/HTTPS流选项
      ffmpegCommand.addInputOption([
        "-re",  // 按照流的原始帧率读取
        "-user_agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36",
        "-timeout", "60",
        "-tls_verify", "0", // 忽略SSL证书验证（兼容旧版FFmpeg）
        "-rw_timeout", "30000000", // 增加读取超时时间(微秒)
        "-protocol_whitelist", "file,http,https,tcp,tls" // 允许的协议列表
      ]);
    } else {
      // 文件输入选项
      ffmpegCommand.addInputOption([
        "-re"  // 按照流的原始帧率读取
      ]);
    }
    
    // 设置转码参数 - 使用h264编码，保证浏览器兼容性
    ffmpegCommand
      .videoCodec("libx264")
      .outputOptions([
        "-preset", "ultrafast", // 最快的编码速度，适合实时流
        "-tune", "zerolatency", // 零延迟调优，适合实时传输
        "-crf", "30", // 较低的CRF值意味着更好的质量
        "-x264-params", "keyint=25:no-scenecut=1", // 保证关键帧间隔，适合流媒体
        "-threads", "2", // 限制线程数，避免过多资源占用
        "-fflags", "+nobuffer", // 减少缓冲
        "-flags", "low_delay", // 低延迟模式
        "-bufsize", "102400k" // 缓冲区大小，适合编码选项
      ])
      
      // 不处理音频
      .noAudio()
      
      // 输出为FLV格式，适合web播放
      .outputFormat("flv")
      
      // 事件处理
      .on("start", (cmd) => {
        console.log(`Stream started. Command: ${cmd}`)
      })
      .on("codecData", (data) => {
        console.log(`Stream codec data: ${JSON.stringify(data)}`);
        // 向客户端发送编码信息
        if (ws.readyState === 1) {
          ws.send(JSON.stringify({ type: 'codec_info', data: data }));
        }
      })
      .on("error", err => {
        console.error(`Error occurred: ${err.message}`);
        // 向客户端发送错误消息
        if (ws.readyState === 1) {
          ws.send(JSON.stringify({ type: 'error', message: err.message }));
        }
        // 延迟关闭以允许可能的重连
        setTimeout(() => {
          if (ws.readyState === 1 || ws.readyState === 0) {
            ws.close();
          }
        }, 1000);
      })
      .on("end", () => {
        console.log("Stream ended");
        ws.close();
      });

    // 将转换后的流通过WebSocket发送到客户端
    ffmpegCommand.pipe(stream);
    
    // 监听WebSocket关闭事件，延迟清理FFmpeg进程
    ws.on('close', () => {
      console.log('WebSocket connection closed, will stop ffmpeg after delay');
      // 设置10秒延迟后停止FFmpeg进程，避免短暂连接断开导致视频流完全中断
      setTimeout(() => {
        console.log('Stopping ffmpeg after delay');
        ffmpegCommand.kill('SIGTERM');
      }, 10000); // 延迟10秒，可以根据需要调整
    });
    
  } catch (error) {
    console.error('Unexpected error:', error);
    if (ws.readyState === 1) {
      ws.send(JSON.stringify({ type: 'error', message: error.message }));
    }
    ws.close();
  }
}